/*
 * Copyright (C) 2006 Roman Krashanitsa
 *
 * @author Roman Krashanitsa
 *
 * This file is part of paparazzi.
 *
 * paparazzi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * paparazzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with paparazzi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */


package pprz.components;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import net.jscience.math.MathFP;
import pprz.data_components.Message;
import pprz.data_components.events.DownlinkMessageEvent;
import pprz.data_components.events.DownlinkMessageEventListener;
import pprz.data_components.parsers.MessageParser;
import pprz.data_components.parsers.MessageParser.MessageFormat;

public class MessageView extends Frame 
implements DownlinkMessageEventListener, 
		   MouseListener, ActionListener, WindowListener {

	protected static final long serialVersionUID = 1L;
	HashMap<Long,MessageFormat> formats; //all message formats
	HashMap<Integer,HashMap<Integer,JPanel> > acHash; //ac->msgid->panel relation
	HashMap<Integer,JPanel> acPanels;
	HashMap<Integer,Plot> figures; // active telemetry plots
	JTabbedPane root; // root pane
	Vector<String> labels; //temp variable
	protected PopupMenu menu; // plot popup menu

	//class to hold calculated (mouse coordinates)->(acid,msgid,field)
	public class MessageCoordinate {
		public int ac; 
		public int msgC; 
		public int msgId; 
		public int msgFld;
		public boolean valid;
	}

	public MessageCoordinate lastMsgClc; //last clicked acid,message,field
	/**
	 * This is the default constructor
	 */
	public MessageView(HashMap<Long,MessageParser.MessageFormat> messageFormats) {
		super();
		if (messageFormats!=null) formats=messageFormats;
		else assert messageFormats == null;
		acHash=new HashMap<Integer,HashMap<Integer,JPanel> >();
		acPanels=new HashMap<Integer,JPanel>();
		figures=new HashMap<Integer,Plot>();
		labels=new Vector<String>();
		menu=new PopupMenu();
		lastMsgClc=new MessageCoordinate();
		initialize();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	protected void initialize() {
		this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
		this.setSize(300, 200);
		this.setTitle("System Messages");
		root=new JTabbedPane();
		root.setTabPlacement(JTabbedPane.TOP);
		this.add(root, null);
		this.addMouseListener(this);
		menu.add("new plot");
		this.add(menu);
		menu.addActionListener(this);
		processing=false;
	}
	
	/**
	 * Downlink message event
	 * shows all incoming telemetry from all airplanes
	 * @return void
	 */	
	boolean processing;
	public void messageEvent(DownlinkMessageEvent ev) {

		if (processing)
		{
			System.out.println("STILL PROCESSING!!!!!");
			System.exit(1);
		}
		processing=true;
		Message m = ev.getMessage();
		//if (m.acRec().state==Aircraft.NOTHING) return;
		HashMap<Integer,JPanel> messages;
		int msgId=m.id();
		int msgC=m.msgClass();
		int ac=(msgC==0)?m.acRec().get_id():-1;
		JPanel acPanel;
		if (!acHash.containsKey(ac)) 
		{
			acHash.put(ac, new HashMap<Integer,JPanel>());
			acPanel=new JPanel();
			acPanel.setLayout(new BoxLayout(acPanel, BoxLayout.Y_AXIS));
			acPanel.setPreferredSize(new Dimension(410, 50));
			
			acPanels.put(ac, acPanel);
			root.addTab(String.valueOf(ac), acPanel);
			JTabbedPane newTab=new JTabbedPane();
			newTab.setTabPlacement(JTabbedPane.LEFT);			
			acPanel.add(newTab, null);
		}
		acPanel=acPanels.get(ac);
		messages=acHash.get(ac);
		if (!formats.containsKey(msgC*0x100000000l+msgId)) return;
		MessageFormat format=formats.get(msgC*0x100000000l+msgId);
		if (format.noClass || format.noId || format.noName || 
		format.noNameField>0 || format.noTypeField>0) return;
		
		JPanel msgPanel; //temp variable
		if (!messages.containsKey(msgId))
		{
			labels.clear();
			for (MessageParser.MessageField field : format.fields)	{ 
				labels.add(field.name); 
			}
			msgPanel=newMessagePanel(labels);
			((JTabbedPane)acPanel.getComponent(0)).
				addTab(format.name, msgPanel); //there is only one component in the panel
			messages.put(msgId, msgPanel);
		}
		
		//System.out.printf("msgc=%d,msgid=%d,n=%d\n",msgC,msgId,format.fields.size());
		msgPanel=messages.get(msgId);
		for (int i=0; i<format.fields.size(); i++)
		{
			if (msgPanel.getComponentCount()!=format.fields.size()*2) 
			{
				System.out.println("Message Format Error..");
				System.out.println("Panel:"+msgPanel.getComponentCount()+" format:"+(format.fields.size()*2));
				System.exit(1);
				return;
			}
			Component c=msgPanel.getComponent(2*i+1);
			if (c.getClass()==Label.class) {
			 Label l=(Label)c;
			 Long[] n=m.getFields().get(i);
			 StringBuffer s=new StringBuffer();
			 if (n.length<=1) s.append(MathFP.toString(n[0]));
			 else {
				 for (int j=0; j<n.length; j++) {	 
					 s.append(MathFP.toString(n[j]));
					 if (j<n.length-1) s.append("; ");
				 }
			 }
			 l.setText(s.toString());
			}
		}		
		
		//resend message to figures
		Collection<Plot> c=figures.values();
		for (Plot p : c) p.messageEvent(ev);
		processing=false;
	}
	
	
	/**
	 * Create a new panel for a message according to message format
	 * 
	 * @return JPanel
	 */

	protected JPanel newMessagePanel(Vector<String> labels)
	{
		JPanel p=new JPanel();
		p.setPreferredSize(new Dimension(20,20));
		p.setLayout(new GridLayout(0,2,3,3));
		p.addMouseListener(this);
		
		for (int i=0;i<labels.size();i++)
		{
			Label l1=new Label(labels.get(i));
			l1.setName(String.valueOf(i));
			p.add(l1); l1.addMouseListener(this);
			Label l2=new Label("0.000");
			l2.setName(String.valueOf(i));
			p.add(l2); l2.addMouseListener(this);
		}
		return p;
	}

	public void mouseClicked(MouseEvent arg0) {
		
	}

	public void mouseEntered(MouseEvent arg0) {
		
	}

	public void mouseExited(MouseEvent arg0) {
		
	}

	/**
	 * Mouse pressed
	 * Calculate active acId,msgId,and field when user clicked
	 * and show popup menu if it was a right-click
	 * @return void
	 */
	public void mousePressed(MouseEvent e) {
//		System.out.println("Mouse pressed");
		if (e.getButton()==MouseEvent.BUTTON3) {
			
			if (e.getComponent().getClass()!=Label.class) return;
			calcLastMessage(e.getComponent());
			//show popup menu
			if (e.isPopupTrigger())
				menu.show(e.getComponent(),e.getX(), e.getY());
		}
	}

	public void mouseReleased(MouseEvent e) {
//		System.out.println("Mouse releazed");
		if (e.getButton()==MouseEvent.BUTTON3) {
	        if (e.isPopupTrigger()) 
	        	menu.show(e.getComponent(),e.getX(), e.getY());
		}
	}

	/**
	 * Calculate active acId,msgId,and field
	 * 
	 * @return MessageCoordinate
	 */
	
	protected MessageCoordinate calcLastMessage(Component cmp)
	{
		// find message and acid when user clicked
		Iterator<MessageFormat> i=formats.values().iterator();
		MessageFormat fmt;
		boolean found=false;

		lastMsgClc.ac=Integer.valueOf(root.getTitleAt(root.getSelectedIndex()));
		JPanel c=(JPanel)root.getSelectedComponent();
		JTabbedPane cc=(JTabbedPane)c.getComponent(0);
		String message=cc.getTitleAt(cc.getSelectedIndex());
		
		while (i.hasNext() && !found)
		{
			fmt=i.next();
			if (fmt.name.compareToIgnoreCase(message)==0)
			{
				found=true;
				lastMsgClc.msgId=fmt.mId;
				lastMsgClc.msgC=fmt.cId;
			}
		}
		lastMsgClc.msgFld=Integer.valueOf(cmp.getName());
		lastMsgClc.valid=found;
		return lastMsgClc;
	}
	
	/**
	 * User wants to plot some message field
	 * @return void
	 */	
	public void actionPerformed(ActionEvent arg0) {
		String s=arg0.getActionCommand();
		int fig;
		if (s.compareToIgnoreCase("new plot")==0)
		{
			System.out.println("executing -new plot-");
			
			if (!lastMsgClc.valid) return;
			
			if (figures.size()<1)
			{
				PopupMenu p=new PopupMenu("add to ...");
				p.addActionListener(this);
				menu.add(p);
			}
			Plot p=new Plot();
			p.setFormats(formats);
			p.addWindowListener(this);
			p.add(lastMsgClc.ac,lastMsgClc.msgId,lastMsgClc.msgFld);
			
			if (figures.size()>0)
			{
				Plot[] plots=new Plot[10];
				plots=figures.values().toArray(plots);
				fig=Integer.valueOf(plots[0].getTitle().substring(6).trim())+1;
			}
			else fig=0;
			p.setTitle("Figure "+fig);
			p.setVisible(true);
			figures.put(figures.size(), p);
			((PopupMenu)menu.getItem(1)).add(p.getTitle());
			
		} else // add data to existing frame
		{
			fig=Integer.valueOf(s.substring(6).trim());
//			System.out.println("adding to figure:"+fig);
			if (!lastMsgClc.valid) return;
			Plot p=figures.get(fig);
			p.add(lastMsgClc.ac,lastMsgClc.msgId,lastMsgClc.msgFld);
			//p.repaint();
		}
	}

	public void windowActivated(WindowEvent arg0) {}

	/**
	 * User closed a plot frame. We need to do some cleanup
	 * @return void
	 */
	public void windowClosed(WindowEvent arg0) {
		
		//check only Plot frames
		if (!arg0.getWindow().getClass().equals(Plot.class)) return;
		
		String title=((Plot)arg0.getWindow()).getTitle();
		if (title.length()<7) return;
		int fig=Integer.valueOf(title.substring(6).trim());
		figures.remove(fig);
		PopupMenu p=(PopupMenu)menu.getItem(1);
		if(p==null) return;
		int i=0;
		boolean found=false;
		while (i<p.getItemCount() && !found)
		{
			if (p.getItem(i).getLabel().compareTo(title)==0)
			{
				p.remove(i);
				found=true;
			}
			if (figures.size()==0) 
				menu.remove(1);
			i++;
		}
	}

	public void windowClosing(WindowEvent arg0) {}

	public void windowDeactivated(WindowEvent arg0) {}

	public void windowDeiconified(WindowEvent arg0) {}

	public void windowIconified(WindowEvent arg0) {}

	public void windowOpened(WindowEvent arg0) {}
	
}


